# include "cmd.h"

static LinkedList *commandList = NULL;
static int done;
static char * commandGenerator (const char *text, int n);
static char ** commandCompletion (const char *text, int a, int b);
static CommandResult * executeCommand (const char *line);
static void showAllCommands();

void setDone () {
	done = 1;
}

Command * newCommand (char *name, cmdFunction func, char *desc) {
	Command *new = (Command *) malloc(sizeof(Command));
	if (!new) logMessage("io.cmd","cmd.c","newCommand",
                        "Memória Insuficiente",CRITICAL);
	new->name = strdup(name);
	new->command = func;
	new->description = strdup(desc);
	return new;
}
void initializeCommandLine (LinkedList *lst) {
	commandList = lst;
	done = 0;
	rl_readline_name = "CommandLine";
	rl_attempted_completion_function = commandCompletion;
	
}
static char ** commandCompletion (const char *text, int start, int end) {
	char **matches = (char **) NULL;
	if (start == 0) {
		matches = rl_completion_matches(text,commandGenerator);
	} else rl_attempted_completion_over = 1; 
	return matches;
}
static list_index = 0;
static len = 0;
static char * commandGenerator (const char *text, int state) {
	char *name;
	Command *aux;
	LinkedList *list = commandList;
	if (!state) {
		list_index = 0;
		len = strlen(text);
	}
	int i;
	for (i=0;i<list_index&&list;i++) list = list->next;
	while (list) {
		list_index++;
		aux = getData(list);
		list = list->next;
		if (!strncmp(aux->name,text,len))
			return strdup(aux->name);
	}
	return ((char *)NULL);
}
CommandResult * newCommandResult (const char *reason, const char *solution, CommandResultType type) {
	CommandResult *res = (CommandResult *) malloc(sizeof(CommandResult));
	if (!res)
		logMessage("io.cmd","cmd.c","newCommandResult",
			"Memória Insuficiente",CRITICAL);
	if (reason != NULL) res->reason = strdup(reason);
	else res->reason = NULL;
	if (solution != NULL) res->solution = strdup(solution);
	else res->solution = NULL;
	res->type = type;
	return res;
}
CommandResult * successCommandResult () {
	return newCommandResult(NULL,NULL,FINE);
}
static CommandResult * commandDoesntExists () {
	return newCommandResult(
		SHOW_COMMAND_DOESNT_EXIST,
		SHOW_COMMAND_DOESNT_EXIST_SOLUTION,
		NOT_EXISTS);
}
static CommandResult * executeCommand (const char *line) {
	int i, argc, res = parseCommand(line);
	CommandResult *cmdR = NULL;
	char *name,**argv;
	LinkedList *lst;
	Command *aux;
	if (res) {
		// leitura com sucesso!
		name = getCommandName();
		if (name) {
			if (!strcmp(name,SPECIAL_COMMAND_LIST)) {
				showAllCommands();
				return successCommandResult();
			}
			else {
				argv = getCommandArguments();
				argc = getNumberOfArguments();
				// procurar pelo name na lista!
				lst = commandList;
				while (lst) {
					aux = getData(lst);
					lst = lst->next;
					if (!strcmp(name,aux->name))
						return (aux->command)(argc,argv);
				}
			}
		}
	}
	return commandDoesntExists();
}
static void showAllCommands() {
	LinkedList *lst = commandList;
	Command *aux;
	while (lst) {
		aux = getData(lst);
		printf(SHOW_COMMAND_PATTERN,aux->name,aux->description);
		lst = lst->next;
	}
}
static void showErrors (CommandResult *res) {
	if (res) {
		switch (res->type) {
			case NOT_EXISTS:
				printf("** Unkown Command **\n%s\n",SHOW_COMMAND_DOESNT_EXIST_SOLUTION);
				break;
			case FEW_ARGUMENTS:
			case OVER_ARGUMENTS:
				printf("** Syntax Error **\nReason: %s\nSolution: %s\n",res->reason,res->solution);
				break;
			case ARGUMENT:
				printf("** Error **\nReason: %s\nSolution: %s\n",res->reason,res->solution);
				break;
		}
	}
	freeCommandResult(res);
}
void freeCommandResult (CommandResult *cmd) {
	if (cmd&&cmd->solution) free(cmd->solution);
	if (cmd&&cmd->reason) free(cmd->reason);
	if (cmd) free(cmd);
}
int isDone () {
	return done;
}
static char *read_line = NULL;
void promptUser () {
	if (read_line) {
		free(read_line);
		read_line = NULL;
	}
	read_line = readline(SHOW_COMMAND_PROMPT);
	if (read_line && *read_line) {
		add_history(read_line);
		CommandResult *res = executeCommand(read_line);
		showErrors(res);
	}
}
void promptUserCicle () {
	while (!done) promptUser();
}

